package com.only4play.practice.pay.payment.kit.plugin;

import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayConstants;
import com.alipay.api.domain.AlipayTradePrecreateModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePrecreateRequest;
import com.alipay.api.response.AlipayTradePrecreateResponse;
import com.ijpay.alipay.AliPayApi;
import com.ijpay.alipay.AliPayApiConfig;
import com.ijpay.alipay.AliPayApiConfigKit;
import com.only4play.codegen.support.constants.ValidStatus;
import com.only4play.codegen.support.model.JsonObject;
import com.only4play.practice.common.enums.OrderState;
import com.only4play.practice.common.enums.pay.PayModeType;
import com.only4play.practice.common.utils.OrderNoGenUtils;
import com.only4play.practice.common.utils.SpringContextUtils;
import com.only4play.practice.pay.payment.core.entity.TradePaymentOrder;
import com.only4play.practice.pay.payment.core.service.ITradePaymentOrderService;
import com.only4play.practice.pay.payment.kit.PaymentPlugin;
import com.only4play.practice.pay.payment.kit.request.PayParamRequest;
import com.only4play.practice.pay.reconcile.core.entity.MerchantApp;
import com.only4play.practice.pay.reconcile.core.model.payconfig.AliPayConfig;
import com.only4play.practice.pay.reconcile.core.model.payconfig.PayConfig;
import com.only4play.practice.pay.reconcile.core.service.IMerchantAppService;
import com.only4play.practice.pay.reconcile.core.strategy.download.AliParamNameConstant;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author mengdy
 */

@Slf4j
public class AliPayPaymentPlugin implements PaymentPlugin {

    private final IMerchantAppService merchantAppService =
            SpringContextUtils.getBean(IMerchantAppService.class);

    private final ITradePaymentOrderService tradePaymentOrderService =
            SpringContextUtils.getBean(ITradePaymentOrderService.class);

    private final OrderNoGenUtils orderNoGenUtils =
            SpringContextUtils.getBean(OrderNoGenUtils.class);



    @Override
    public JsonObject<Object> unifiedOrder(HttpServletRequest request, PayParamRequest payParam) {

        if (!PayModeType.of(payParam.getPayModeType()).isPresent()){
            throw new IllegalArgumentException("支付类型不支持");
        }
        // 根据渠道编码获取支付配置
        String cpId = payParam.getChannelPointCode();
        MerchantApp merchantApp = merchantAppService.findByChanPointId(cpId);
        log.info("商户配置信息:{}",merchantApp);
        if (ObjectUtil.isNull(merchantApp)){
            throw new IllegalArgumentException("商户支付配置信息不存在");
        }

        PayConfig payConfig = merchantApp.getPayConfig();
        AliPayConfig aliPayConfig = (AliPayConfig) payConfig;
        log.info("支付配置信息:{}",aliPayConfig);
        // TODO 初始化支付宝网关参数
        AliPayApiConfig aliPayApiConfig = null;
        try {
            String interfaceSignType = aliPayConfig.getInterfaceSignType();
            // 判断加签方式
            if (ObjUtil.equals(
                    AliParamNameConstant.INTERFACE_SIGN_TYPE_VALUE_SECRET,
                    interfaceSignType)){
                log.info("使用密钥模式");
                aliPayApiConfig = AliPayApiConfig.builder()
                        // 支付宝应用id
                        .setAppId(aliPayConfig.getAppId())
                        // 设置商户应用私钥
                        .setPrivateKey(aliPayConfig.getPrivateKey())
                        .setAliPayPublicKey(aliPayConfig.getPublicKey())
                        .setCharset("UTF-8")
                        .setServiceUrl(ObjUtil.equals(aliPayConfig.getIsSandbox(), ValidStatus.VALID) ?
                                AliParamNameConstant.ALIPAY_SERVER_GATEWAY_SANDBOX :
                                AliParamNameConstant.ALIPAY_SERVER_GATEWAY_PROD)
                        .setSignType("RSA2")
                        .build();

            }else if (ObjUtil.equals(
                    AliParamNameConstant.INTERFACE_SIGN_TYPE_VALUE_CERT,
                    interfaceSignType
            )){
                log.info("使用证书模式");
                aliPayApiConfig = AliPayApiConfig.builder()
                        // 支付宝应用id
                        .setAppId(aliPayConfig.getAppId())
                        // 设置商户应用私钥
                        .setAppCertPath(aliPayConfig.getAppCertB64())
                        .setAliPayCertPath(aliPayConfig.getAlipayCertB64())
                        .setAliPayRootCertPath(aliPayConfig.getAlipayRootCertB64())
                        .setCharset("UTF-8")
                        .setServiceUrl(ObjUtil.equals(aliPayConfig.getIsSandbox(), ValidStatus.VALID) ?
                                AliParamNameConstant.ALIPAY_SERVER_GATEWAY_SANDBOX :
                                AliParamNameConstant.ALIPAY_SERVER_GATEWAY_PROD)
                        .setSignType("RSA2")
                        .buildByCert();
            }else {
                throw new RuntimeException("不支持的加签方式");
            }

            AliPayApiConfigKit.setThreadLocalAliPayApiConfig(aliPayApiConfig);

            switch (PayModeType.of(payParam.getPayModeType()).get()){
                case ALIPAY_PAY_APP:{
                    throw new IllegalArgumentException("咱不支持该支付类型");
                }
                case ALIPAY_PAY_H5:{
                    log.info("H5支付.....");
                    return null;
                }
                case ALIPAY_PAY_SCAN_CODE:{
                    log.info("微信扫码支付.....");
                    return scanCodePay(request,payParam,aliPayConfig);
                }
                default:{
                    throw new IllegalArgumentException("支付类型不支持");
                }
            }
        }catch (Exception e){
            throw new IllegalArgumentException("支付配置信息异常");
        }
    }

    private JsonObject<Object> scanCodePay(HttpServletRequest request,
                                           PayParamRequest payParam,
                                           AliPayConfig aliPayConfig) throws AlipayApiException {

        log.info("扫码支付.....");
        // String genOrderNo = orderNoGenUtils.genOrderNo(payParam.getServiceName());
        String genOrderNo = System.currentTimeMillis() + "";
        TradePaymentOrder paymentOrder = createPaymentOrder(payParam,genOrderNo);
       log.info("创建支付流水订单:{}",paymentOrder);
       // TODO 创建支付流水订单
       boolean save = tradePaymentOrderService.save(paymentOrder);
       if (!save){
           throw new IllegalArgumentException("创建支付流水订单失败");
       }
        // TODO 构造支付宝网关业务请求参数
        AlipayTradePrecreateModel bizContent = new AlipayTradePrecreateModel();
        bizContent.setSubject(payParam.getTitle());
        bizContent.setBody(payParam.getDescBody());
        bizContent.setTotalAmount(String.valueOf(payParam.getPayAmount()));
        bizContent.setTimeoutExpress("5m");
        bizContent.setOutTradeNo(paymentOrder.getMchTradChanOrderNo());

        // 构建支付成功后支付宝通知的回调地址
        String notifyUrl = buildNotifyUrl(aliPayConfig.getDomain(),aliPayConfig.getChannelType().getCode(), paymentOrder.getChanTraPayOrderNo());
        AlipayTradePrecreateResponse response = null;
        // 是否服务商模式
        if (ObjUtil.equals(aliPayConfig.getIsServiceProvider(), ValidStatus.VALID) && StrUtil.isNotEmpty(aliPayConfig.getAppAuthToken())){
            // TODO 使用这种方式，直接调用相应接口
            //卖家支付宝用户 ID。 如果该值为空，则默认为商户签约账号对应的支付宝用户 ID。不允许收款账号与付款方账号相同
            bizContent.setSellerId(aliPayConfig.getSubAppId());
            response = AliPayApi.tradePrecreatePayToResponse(bizContent, notifyUrl,aliPayConfig.getAppAuthToken());
        }else {
            // 直连模式
            // TODO 可以使用这种请求方式，自己构建请求
            AlipayTradePrecreateRequest aliRequest = new AlipayTradePrecreateRequest();
            aliRequest.setBizModel(bizContent);
            aliRequest.setNotifyUrl(notifyUrl);
            // 设置用户支付成功后跳转的页面
            // aliRequest.setReturnUrl("http://www.baidu.com");
            response = AliPayApi.execute(aliRequest);
        }
        if (ObjectUtil.isNotNull(response) && response.isSuccess()){
            log.info("支付宝支付响应:{}",response);
            Map<String, String> map = new HashMap<>();
            map.put("qr_code_url",response.getQrCode());
            map.put("out_trade_no",response.getOutTradeNo());
            return JsonObject.success(map);
        }
        log.info("调用失败，返回码:{}，具体信息是:{}", response.getCode(), response.getMsg());
        throw new RuntimeException();
    }


    /**
     * 支付宝会通过 POST 请求的形式将支付结果作为参数通知到商家系统
     * 异步回调响应状态码 (HTTP 状态码) 为 200 时表示异步通知成功，返回码为 404 或 500 时则表示服务器内部错误，需要商家自行排查
     * 异步通知交互时，如果支付宝收到的应答不是 success ，支付宝会认为通知失败，会通过一定的策略定期重新发起通知，支付宝针对同一条异步通知重试时，异步通知参数中的 notify_id 是不变的
     *
     */
    @Override
    public String unifiedCallback(HttpServletRequest request, Map<String, String> params) {
        log.info("支付宝回调参数 = {}", params);

        // 通知类型
        String notify_type = params.get("notify_type");
        if (!StrUtil.equals(notify_type,"trade_status_sync")){
            log.info("回调类型异常 notify_type = {}",notify_type);
            return buildAliNotifyResponse(false);
        }

        String trade_status = params.get("trade_status");
        if (StrUtil.isEmpty(trade_status)){
            log.info("回调参数异常 trade_status = {}",trade_status);
            return buildAliNotifyResponse(false);
        }

        String out_trade_no = params.get("out_trade_no");
        if (StrUtil.isEmpty(out_trade_no)){
            log.info("回调参数异常 out_trade_no = {}",out_trade_no);
            return buildAliNotifyResponse(false);
        }
        TradePaymentOrder paymentOrder = tradePaymentOrderService.findByChanTraPayOrderNo(out_trade_no);
        if (ObjUtil.isNull(paymentOrder)){
            log.info("回调参数异常 out_trade_no = {}",out_trade_no);
            return buildAliNotifyResponse(false);
        }

        //
        String channelPointCode = paymentOrder.getChannelPointCode();
        MerchantApp merchantApp = merchantAppService.findByChanPointId(channelPointCode);
        if (ObjUtil.isNull(merchantApp)){
            log.info("回调参数异常 channelPointCode = {}",channelPointCode);
            return buildAliNotifyResponse(false);
        }
        AliPayConfig aliPayConfig = JSONObject.parseObject(merchantApp.getChannelPayConfig(), AliPayConfig.class);
        // TODO 回调结果验签
        boolean verifyResult = false;
        try {
            // 注意 服务商模式 与 普通商户直接模式 的回调结果验签方式有区别，并且需要与统一下单的签名类型一致
            if (ObjectUtil.equals(aliPayConfig.getIsServiceProvider(),ValidStatus.VALID)){
                log.info("服务商模式");
                //服务商模式  TODO 检查服务商配置
                verifyResult = AlipaySignature.rsaCertCheckV1(params, aliPayConfig.getAlipayCertB64(), AlipayConstants.CHARSET_UTF8,AlipayConstants.SIGN_TYPE_RSA2);
            }else {
                log.info("直连模式");
                // 直连模式 TODO 检查直连模式配置
                verifyResult = AlipaySignature.rsaCheckV1(params, aliPayConfig.getPublicKey(),  AlipayConstants.CHARSET_UTF8,AlipayConstants.SIGN_TYPE_RSA2);
            }
        }catch (Exception e){
            log.error("验签失败",e);
        }
        if (!verifyResult){
            return buildAliNotifyResponse(false);
        }
        log.info("支付成功异步通知请求验签成功，继续校验订单业务");
        BigDecimal multi100 = new BigDecimal(100);
        // 校验支付金额
        if (!ObjUtil.equals(paymentOrder.getPayAmount(),new BigDecimal(params.get("total_amount")).multiply(multi100).longValue())){
            log.info("支付金额异常，渠道支付金额:{},订单金额:{}",params.get("total_amount"),paymentOrder.getPayAmount());
            return buildAliNotifyResponse(false);
        }
        // todo 校验通知中的 seller_id（或者 seller_email) 是否为 out_trade_no 这笔单据的对应的操作方（有的时候，一个商家可能有多个 seller_id/seller_email）

        // 根据交易状态做幂等性处理，防止重复通知
        switch (trade_status){
            //交易创建，等待买家付款
            case "WAIT_BUYER_PAY":
                log.info("交易状态异常 trade_status = {}",trade_status);
                return buildAliNotifyResponse(false);
            case "TRADE_CLOSED":
                log.info("由于用户未支付，导致超时关闭 trade_status = {}",trade_status);
                // TODO 订单超时关闭业务处理
                if (true){
                    return buildAliNotifyResponse(true);
                }else {
                    return buildAliNotifyResponse(false);
                }
            // 以下两种都是支付成功
            case "TRADE_SUCCESS":
            // 交易结束，不可退款
            case "TRADE_FINISHED":{
                log.info("交易支付成功 trade_status = {}",trade_status);
                // TODO 判断订单状态是否已支付，避免重复处理
                OrderState status = paymentOrder.getStatus();
                if (OrderState.SUCCESS.equals(status)){
                    return buildAliNotifyResponse(true);
                }
                // 更新订单支付时间
                paymentOrder.setPayTime(new Date());

                String trade_no = params.get("trade_no");
                paymentOrder.setChanTraPayOrderNo(trade_no);
                boolean update = tradePaymentOrderService.updateById(paymentOrder);
                // TODO 发送MQ通知业务服务，订单支付成功，等到业务处理完成，再更新订单状态为SUCCESS还是在哪一步更新状态为好？
                // sendMessage(paymentOrder);
                return buildAliNotifyResponse(true);
            }
        }
        return buildAliNotifyResponse(false);
    }


}
